﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using SHARED;

namespace IPL
{
    public enum NodeType
    {
        Rule,
        Token
    }

    public enum TokenType
    {
        End,
        Symbol,
        Keyword,
        Identifier,
        ShapeVar,
        Constant,
        Color,
        Numeric
    }

    public abstract class Node
    {
        public NodeType NodeType { get; set; }
        public IList<Node> Children { get; set; }

        protected Node(NodeType type)
        {
            this.NodeType = type;
        }
    }

    public class Rule : Node
    {
        public string Name { get; set; }

        public Rule(string name, NodeType type)
            : base(NodeType.Rule)
        {
            Name = name;
            NodeType = type;
        }
    }

    public abstract class Token : Node
    {
        public TokenType TokenType { get; set; }
        public string Lexem { get; set; }
        public int LineNum { get; set; }
        public string Value { get; set; }

        public Token(string lexem, int lineNum, TokenType tType)
            : base(NodeType.Token)
        {
            this.TokenType = tType;
            Lexem = lexem;
            LineNum = lineNum;
            setValue(lexem);
        }

        protected Token setValue(string value) { this.Value = value; return this; }

        public static Token New(string lexem, int lineNum)
        {
            if (Regex.IsMatch(lexem, @"^[=,;:*<>(){}[\]-]$")) // Symbols
            {
                return new SymbolToken(lexem, lineNum, TokenType.Symbol).setValue(lexem);
            }
            else if (Regex.IsMatch(lexem, @"^(canvas|brush|polygon|ellipse|ppellipses|ppline)$")) // Keywords
            {
                return new KeywordToken(lexem, lineNum, TokenType.Keyword);
            }
            //else if (Regex.IsMatch(lexem, @"^[a-z0-9_]+$")) // Identifier
            //{
            //    return new IdentifierToken(lexem, lineNum, TokenType.Identifier);
            //}
            else if (Regex.IsMatch(lexem, @"^@[a-z0-9_]+$")) // ShapeVar
            {
                return new ShapeVarToken(lexem, lineNum, TokenType.ShapeVar);
            }
            else if (Regex.IsMatch(lexem, @"^\$[0-9a-z_]+$")) // Constant
            {
                return new ConstantToken(lexem, lineNum, TokenType.Constant);
            }
            else if (Regex.IsMatch(lexem, @"^#[0-9a-f]{6}$")) // HexColor
            {
                return new ColorToken(lexem, lineNum, TokenType.Color).setValue("TEMP");
            }
            //else if (Regex.IsMatch(lexem, @"^r=([1-2][0-5][0-5]|[1-9][0-9]|[0-9])$")) // RGBColor
            //{
            //    return new ColorToken(lexem, lineNum, TokenType.Color);
            //}
            else if (Regex.IsMatch(lexem, @"^[-]?[0-9]+$")) // Integer
            {
                return new NumericToken(lexem, lineNum, TokenType.Numeric).setValue(float.Parse(lexem));
            }
            else if (Regex.IsMatch(lexem, @"^[-]?[0-9]*\.[0-9]+$")) // Float 
            {
                return new NumericToken(lexem, lineNum, TokenType.Numeric).setValue(float.Parse(lexem));
            }
            else if (Regex.IsMatch(lexem, @"^[-]?[0-9]+/[1-9][0-9]*$")) // Fraction
            {
                string[] args = lexem.Split('/');
                return new NumericToken(lexem, lineNum, TokenType.Numeric)
                    .setValue((float.Parse(args[0]) / float.Parse(args[1])).ToString());
            }
            //else if (Regex.IsMatch(lexem, @"^[-]?[0-9]+/[1-9][0-9]*$")) // Point
            //{
            //    string[] args = lexem.Split('/');
            //    return new NumericToken(lexem, lineNum, TokenType.Numeric)
            //        .setValue(float.Parse(args[0]) / float.Parse(args[1]));
            //}
            else
            {
                throw new Exception(String.Format("No Token matching lexem \"{0}\" on line {1}", lexem, lineNum));
            }
        }
    }

    public class EndToken : Token
    {
        public EndToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, tType) { }
    }

    public class ConstantToken : Token
    {
        public ConstantToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, tType) { }
    }

    public class SymbolToken : Token
    {
        public SymbolToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, TokenType.Symbol) { }
    }

    public class KeywordToken : Token
    {
        public KeywordToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, TokenType.Keyword) { }
    }

    public class IdentifierToken : Token
    {
        public IdentifierToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, TokenType.Identifier) { }
    }

    public class ShapeVarToken : Token
    {
        public ShapeVarToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, tType) { }
    }

    public class ColorToken : Token
    {
        new public Object Value { get; set; }
        public ColorToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, tType) { }
        public Token setValue(Object value) { this.Value = value; return this; }
    }

    public class NumericToken : Token
    {
        public float Value { get; set; }
        public NumericToken(string lexem, int lineNum, TokenType tType)
            : base(lexem, lineNum, tType) { }
        public Token setValue(float value) { this.Value = value; return this; }
    }
}